﻿//-----------------------------------------------------------------------
// <copyright file="GetInstalledProducts.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Simon Ransom</author>
// <email></email>
// <date>2004-03-23</date>
// <summary>Get installed products</summary>
//-----------------------------------------------------------------------

namespace Microsoft.Sdc.Tasks
{
  using System;
  using System.Xml;
  using System.Globalization;
  using System.IO;
  using System.Collections;
  using System.Collections.Generic;
  using System.Text;
  using Microsoft.Build.Framework;
  using Microsoft.Build.Utilities;
  using Microsoft.Sdc.Tasks.Configuration.InstalledProducts;

  /// <summary>
  /// Gets a list of installed products on the specified machine
  /// </summary>
  /// <remarks>
  /// <code><![CDATA[ <GetInstalledProducts MachineName="machineName" Username="username" Password="password" InstalledProductsXml="installedProductsXml">
  ///                     <Output TaskParameter="InstalledProductsXml" ItemName="InstalledProductsXmlItem" /> /> 
  ///                 </GetInstalledProducts> />]]></code>
  /// <para>where:</para>
  /// <para><i>machineName</i></para>
  /// <para>The name of the remote machine or "." for local machine. Defaults to "."</para>
  /// <para><i>username</i></para>
  /// <para>The username to be used for the connection operation.</para>
  /// <para><i>password</i></para>
  /// <para>The password to be used for the connection operation.</para>
  /// </remarks>
  /// <example>
  /// <code><![CDATA[
  /// <Project>
  ///     <Target Name="Test" >
  ///         <GetInstalledProducts
  ///             MachineName="remote"
  ///             Username="myDomain\myAuthorisedUser"
  ///             Password="123$abc>
  ///             <Output TaskParameter="InstalledProductsXml" ItemName="InstalledProductsXmlItem" >
  ///         </GetInstalledProducts>            
  ///     </Target>
  /// </Project>
  /// ]]></code>    
  /// </example>
  public class CheckInstalled : TaskBase
  {
    private ITaskItem[] products;
    private ITaskItem[] components;
    private ITaskItem[] productResults;
    private ITaskItem[] componentResults;
    private int productsNotMatching = 0;
    private int componentsNotMatching = 0;

    /// <summary>
    /// Performs the action of this task.
    /// </summary>
    protected override void InternalExecute()
    {
      if (products != null && products.Length > 0)
      {
        ValidateProducts();
      }
      if (components != null && components.Length > 0)
      {
        ValidateComponents();
      }
    }

    private void ValidateProducts()
    {
      InstalledProductManager mgrProducts = new InstalledProductManager();
      ProductList installedProducts = mgrProducts.GetInstalledProducts(".", null, null);

      List<TaskItem> results = new List<TaskItem>();

      IEnumerator productEnumerator = this.products.GetEnumerator();

      // Loop through each of the names of the required products testing against installed products
      while (productEnumerator.MoveNext())
      {
        ITaskItem product = (ITaskItem)productEnumerator.Current;

        string productName = product.ItemSpec;
        string productNameOperator = product.GetMetadata("NameComparisonOperator");
        string productVersion = product.GetMetadata("Version");
        string productVersionOperator = product.GetMetadata("VersionComparisonOperator");
        string productUnaryOperator = product.GetMetadata("UnaryOperator");

        // Name Comparison
        BinaryComparisonOperator productNameComparisonOperator =
          (productNameOperator != null && productNameOperator != String.Empty && productNameOperator.Length > 0) ?
          (BinaryComparisonOperator)Enum.Parse(typeof(BinaryComparisonOperator), productNameOperator) :
          BinaryComparisonOperator.Equals;

        // Version Comparison
        BinaryComparisonOperator productVersionComparisonOperator =
            (productVersionOperator != null && productVersionOperator != String.Empty && productVersionOperator.Length > 0) ?
            (BinaryComparisonOperator)Enum.Parse(typeof(BinaryComparisonOperator), productVersionOperator) :
            BinaryComparisonOperator.Equals;

        // Negate?
        UnaryLogicalOperator unaryOp =
            (productUnaryOperator != null && productUnaryOperator != String.Empty && productUnaryOperator.Length > 0) ?
            (UnaryLogicalOperator)Enum.Parse(typeof(UnaryLogicalOperator), productUnaryOperator) :
            UnaryLogicalOperator.None;

        Criteria criteria = new Criteria();
        mgrProducts.AddForAnd(
            criteria,
            new Criterion(
            typeof(Product).GetProperty("Name"),
            new StringComparisonChecker(productNameComparisonOperator),
            productName
            ));

        // If a version has been specified, always perform equals match (alhough wildcards are allowed)
        if (productVersion != null && productVersion != String.Empty && productVersion.Length > 0)
        {
          mgrProducts.AddForAnd(
              criteria,
              new Criterion(
              typeof(Product).GetProperty("Version"),
              new VersionComparisonChecker(productVersionComparisonOperator),
              productVersion
              ));
        }

        Product matchingProduct = mgrProducts.GetMatchingInstalledProduct(criteria, installedProducts);
        TaskItem result = new TaskItem();

        if (matchingProduct != null)
        {
          result.ItemSpec = matchingProduct.Name + ", " + matchingProduct.Version;

          if (unaryOp == UnaryLogicalOperator.Not)
          {
            result.SetMetadata("Status", "Product installed that should not be installed.");
            this.productsNotMatching++;
          }
          else
          {
            result.SetMetadata("Status", "Product installed.");
          }
        }
        else
        {
          result.ItemSpec = criteria.ToString();

          if (unaryOp == UnaryLogicalOperator.Not)
          {
            result.SetMetadata("Status", "The product criteria was correctly not found.");
          }
          else
          {
            result.SetMetadata("Status", "The product could not be found matching the criteria.");
            this.productsNotMatching++;
          }
        }

        results.Add(result);
      }
      this.productResults = results.ToArray();
    }

    private void ValidateComponents()
    {
      InstalledComponentsManager mgrComponents = new InstalledComponentsManager();
      ComponentList installedComponents = mgrComponents.GetInstalledComponents();

      List<TaskItem> results = new List<TaskItem>();

      IEnumerator componentEnumerator = this.components.GetEnumerator();

      // Loop through each of the names of the required products testing against installed products
      while (componentEnumerator.MoveNext())
      {
        ITaskItem component = (ITaskItem)componentEnumerator.Current;

        string componentName = component.ItemSpec;
        string componentNameOperator = component.GetMetadata("NameComparisonOperator");
        string componentUnaryOperator = component.GetMetadata("UnaryOperator");

        // Name Comparison
        BinaryComparisonOperator componentNameComparisonOperator =
          (componentNameOperator != null && componentNameOperator != String.Empty && componentNameOperator.Length > 0) ?
          (BinaryComparisonOperator)Enum.Parse(typeof(BinaryComparisonOperator), componentNameOperator) :
          BinaryComparisonOperator.Equals;

        // Negate?
        UnaryLogicalOperator unaryOp =
            (componentUnaryOperator != null && componentUnaryOperator != String.Empty && componentUnaryOperator.Length > 0) ?
            (UnaryLogicalOperator)Enum.Parse(typeof(UnaryLogicalOperator), componentUnaryOperator) :
            UnaryLogicalOperator.None;

        Criteria criteria = new Criteria();
        mgrComponents.AddForAnd(
            criteria,
            new Criterion(
            typeof(Component).GetProperty("Id"),
            new StringComparisonChecker(componentNameComparisonOperator),
            componentName
            ));

        Component matchingComponent = mgrComponents.GetMatchingInstalledComponent(criteria, installedComponents);
        TaskItem result = new TaskItem();

        if (matchingComponent != null)
        {
          result.ItemSpec = matchingComponent.Id + ", " + matchingComponent.Description;

          if (unaryOp == UnaryLogicalOperator.Not)
          {
            result.SetMetadata("Status", "Component installed that should not be installed.");
            this.componentsNotMatching++;
          }
          else
          {
            result.SetMetadata("Status", "Component installed.");
          }
        }
        else
        {
          result.ItemSpec = criteria.ToString();

          if (unaryOp == UnaryLogicalOperator.Not)
          {
            result.SetMetadata("Status", "The component criteria was correctly not found.");
          }
          else
          {
            result.SetMetadata("Status", "The component could not be found matching the criteria.");
            this.componentsNotMatching++;
          }
        }

        results.Add(result);
      }
      this.componentResults = results.ToArray();
    }

    public ITaskItem[] Products
    {
      get
      {
        return this.products;
      }
      set
      {
        this.products = value;
      }
    }

    public ITaskItem[] Components
    {
      get
      {
        return this.components;
      }
      set
      {
        this.components = value;
      }
    }

    [Output]
    public ITaskItem[] ProductResults
    {
      get
      {
        return this.productResults;
      }
      set
      {
        this.productResults = value;
      }
    }

    [Output]
    public ITaskItem[] ComponentResults
    {
      get
      {
        return this.componentResults;
      }
      set
      {
        this.componentResults = value;
      }
    }

    [Output]
    public int ProductMatchFailedCount
    {
      get
      {
        return this.productsNotMatching;
      }
    }

    [Output]
    public int ComponentMatchFailedCount
    {
      get
      {
        return this.componentsNotMatching;
      }
    }

    /// <summary>
    /// Initializes a new instance of the CheckInstalled class.
    /// </summary>
    public CheckInstalled()
    {
    }
  }
}